// MatricesBasis.cpp
// Implementation of matrix creation from input, print matrix, get matrix and it's column/row number
// Implementation of multiplication, inversion, transpose, access rows/columns as vectors
 

#include "MatricesLib.h"

using namespace std;

namespace Matrices
{
	float element[16];
	float matrix[4][4];
	int row = 4;
	int col = 4;
	int mid = 4;
	float vector[4];


	////////////////////////////////
	/* Matrix Constructor
	*/
	////////////////////////////////
	MyMatrices::MyMatrices(){}
	MyMatrices::MyMatrices(float m00, float m01, float m02, float m03,
               float m10, float m11, float m12, float m13,
               float m20, float m21, float m22, float m23,
               float m30, float m31, float m32, float m33)
	{}
	MyMatrices::MyMatrices(float *elements){}



	////////////////////////////////
	/* Read matrices from the input
	   The new created matrix is stored in **matrix
	*/
	////////////////////////////////
	void MyMatrices::ReadMatrix()
	{
		//cout<<"Please enter the number of rows in Matrix: ";
		//cin>>row;
		//cout<<endl;
		//cout<<"Please enter the number of columns in Matrix: ";
		//cin>>col;
		//cout<<endl;

		//initialize the Matrix and enter the elements
		//matrix = new float*[row];
		//	for(int i = 0; i < row; i++)
		//	{ matrix[i] = new float[col]; }

		for(int i = 0; i < row; i++){ 
			cout<<"Please enter the "<< i+1 <<" row of Matrix 1: "<<endl;
			cout<<"(Only the first "<< col <<" members are accepted!)"<<endl;
			for (int j = 0; j < col; j++){
				cin>> matrix[i][j];
			}
			cin.sync(); 
			cout<<endl;
		}

	}



	///////////////////////////
	/* Multiply two matrices: **matrix1 and **matrix2 to **matrix
	   need to specify the new matrix's row(matrix1's row) and column(matrix2's column)
	   the one also need to be specified: mid(matrix1's column or matrix2's row, must be the same)
	*/
	///////////////////////////
	float (*MyMatrices::MultiplyMatrix(float **matrix1, float **matrix2))
	{
		//multiplication
		for(int i = 0; i< row; i++){
			for(int j = 0; j < col; j++){
				matrix[i][j] = 0;
				for(int k = 0; k < mid; k++)
					matrix[i][j] = matrix[i][j] + matrix1[i][k] * matrix2[k][j];
			}	
		}

		int k = 0;
		for(int i = 0; i < row; i++){
			for(int j = 0; j < col; j++){
				element[k] = matrix[i][j];
				k++;
			}
		}
		return element;
	}


	/////////////////////////
	/* Transpose Matrix: from **matrix1 to **matrix
	   must specify the row and column
	*/
	////////////////////////
	float (*MyMatrices::TransposeMatrix(float **matrix1))
	{
		//transpose
		for(int i = 0; i < row; i++){
			for(int j = 0; j < col; j++){
				matrix[j][i] = matrix1[i][j];
			}
		}

		int k = 0;
		for(int i = 0; i < row; i++){
			for(int j = 0; j < col; j++){
				element[k] = matrix[i][j];
				k++;
			}
		}
		return element;
	}


	/////////////////////////////
	/* Inverse Matrix
	   Step 1/3 - Calculate the cofactor of element (row,col)
	   invoked by InverseMatrix method
	*/
	/////////////////////////////
	int MyMatrices::GetMinor(float **src, float **dest, int row, int col, int order)
	{
		//indicate which col and row is being copied to destination
		int colCount = 0;
		int rowCount = 0;
		for (int i = 0; i < order; i++){
			if(i != row){
				colCount = 0;
				for(int j = 0; j < order; j++){
					//when j is not the element
					if(j != col){
						dest[rowCount][colCount] = src[i][j];
						colCount++;
					}
				}
			rowCount++;
			}
		}
		return 1;
	}


	//////////////////////////
	/* Inverse Matrix
	   Step 2/3 - Calculate the determinant recursively
	   invoked by InverseMatrix method
	*/
	//////////////////////////
	float MyMatrices::CalculateDeterminant(float **mat, int order)
	{
		//stop the recursion when matrix is a single element
		if(order == 1)
			return mat[0][0];
		//determinant
		float det = 0;
		//allocate	the cofactor matrix
		float **minor = new float *[order-1];
		for(int i = 0; i < order - 1; i++)
			minor[i] = new float[order-1];
		for(int i = 0; i < order; i++ ) 
		{
			//get minor of element (0,i)
			GetMinor(mat,minor,0,i,order);
			//recursion: detA = a_11(-1)^(1+1)*detS_11 + a_12(-1)^(1+2)*detS_12 + ... + a_1n(-1)^(1+n)*detS_1n
			det += (float)pow(-1.0, i) * mat[0][i] * CalculateDeterminant(minor,order-1);
		}
		//release memory
		for(int i = 0; i < order - 1; i++)
			delete []minor[i];
		delete []minor;
		//get the determinant
		return det;
	}


	////////////////////////////////
	/* Inverse Matrix: from **matrix1 to **matrix
	   Step 3/3 - Inversion of the matrix: from matrix1 to matrix
	   param: int order = row = column (square matrix)
	   need to call methods of GetMinor and CalculateDeterminant
	*/
	////////////////////////////////
	float (*MyMatrices::InverseMatrix(float **matrix1))
	{
		int order = 4;
		//get the determinant
		double det = 1.0/CalculateDeterminant(matrix1,order);
		//memory allocation
		float *temp = new float[(order-1)*(order-1)];
		float **minor = new float *[order-1];
		for(int i = 0; i < order - 1; i++)
			minor[i] = temp + (i * (order - 1));
		for(int j = 0; j < order; j++){
			for(int i = 0; i< order; i++){
				//get the cofactor (matrix) of matrix1(j,i)
				GetMinor(matrix1,minor,j,i,order);
				matrix[i][j] = (float)det * CalculateDeterminant(minor,order-1);
				if((i + j)%2 == 1)
					matrix[i][j] = -matrix[i][j];
			}
		}
		//release memory
		delete []minor[0];
		delete []minor;
		delete temp;

		int k = 0;
		for(int i = 0; i < row; i++){
			for(int j = 0; j < col; j++){
				element[k] = matrix[i][j];
				k++;
			}
		}
		return element;
	}


	/////////////////////////////////////
	/* Access Rows or Columns as Vectors
	   row and col indicates that matrix1 is a row by col matrix
	   vr and vc represents which row or column should be accessed 
	*/ 
	/////////////////////////////////////
	float (*MyMatrices::AccessMatrix(int vr, int vc, float **matrix1))
	{
		//store a column as a vector	
		if(vr <= 0){
			if(vc > col) {
				cout << "Can't access an invalid column "<< vc <<endl;
			}
			else{
				for(int i = 0; i < row; i++){
					for(int j = 0; j < col; j++){
						vector[i] = matrix1[i][vc-1];
					}
				}
			}
		}
		//store a row as a vector
		else{
			if(vr > row) {
				cout << "Can't access an invalid row "<< vr <<endl;
			}
			else{
				for(int i = 0; i < col; i++){
					for(int j = 0; j < row; j++){
						vector[i] = matrix1[vr-1][i];
					}
				}
			}
		}
		return vector;
	}


}

